#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

template<class T>
class BlockQueue
{
public:
    BlockQueue(const int& capacity = 5)
    :_capacity(capacity)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_pcond,nullptr);
        pthread_cond_init(&_ccond,nullptr);
    }

    //生产者调用push
    void push(const T& in) //输入参数，const&
    {
        //加锁，保证共享资源的安全
        pthread_mutex_lock(&_mutex);
        //如果队列不满，则生产，满了则阻塞
        // if(_q.size() == _capacity)
        // 注意2：这里使用while来充当条件判断，不能用if。
        // 这重新唤醒后，会再次判断是否能进行生产，假如有10个生产，但只有1个消费者，消费者消费一个后，
        // 时唤醒10个生产者生产，那么用if会导致10个生产者都去生产。
        while(_q.size() == _capacity)  
        {
            // 注意1：
            // pthread_cond_wait这个函数的第二个参数，必须是我们正在使用的互斥锁！
            // 1. 该函数调用的时候，会以原子性的方式，将锁释放，并将自己挂起
            // 2. 该函数在唤醒返回的时候，会自动的重新获取传入的锁，只要该函数返回成功了，就一定重新获取了锁
            pthread_cond_wait(&_pcond, &_mutex); //阻塞
        }
        // 走到这里一定没满
        _q.push(in); //生产
        // 走到这里，可以保证队列中一定存在元素，可供消费者消费
        // 注意3：
        // pthread_cond_signal函数可以放在临界区内部，也可以放在外部。
        pthread_cond_signal(&_ccond); // 这里可以加一定的策略，比如当库存超1/3时再通知消费者消费等。
        pthread_mutex_unlock(&_mutex);
    }

    //消费者调用pop
    void pop(T* out) //输出型参数，*
    {
        //加锁，保证共享资源的安全
        pthread_mutex_lock(&_mutex);
        //如果队列不空，则消费，空了则阻塞
        while(_q.empty())    //空为true
        {
            pthread_cond_wait(&_ccond, &_mutex); //阻塞
        }
        *out = _q.front(); //消费
        _q.pop(); 
        //走到这里，可以保证队列中一定不满，可让生产者生产
        pthread_cond_signal(&_pcond); // 这里可以加一定的策略，比如当库存少于1/3时再通知生产者生产等。
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }
private:
    std::queue<T> _q;
    int _capacity; //最大容量
    pthread_mutex_t _mutex; //生产者和消费者都会访问_q,用来保证共享资源的安全
    pthread_cond_t _pcond; //生产者对应的条件变量，当队列满时，不能再生产，
    pthread_cond_t _ccond; //消费者对应的条件变量，当队列空时，不能再消费，
};